package com.fbs.wowo.finance.service.imp;

import com.fbs.wowo.essay.entity.Essay;
import com.fbs.wowo.essay.mapper.EssayMapper;
import com.fbs.wowo.exception.*;
import com.fbs.wowo.finance.entity.Payment;
import com.fbs.wowo.finance.mapper.PaymentMapper;
import com.fbs.wowo.finance.service.BillService;
import com.fbs.wowo.finance.service.PayService;
import com.fbs.wowo.finance.util.*;
import com.fbs.wowo.finance.vo.Buy;
import com.fbs.wowo.finance.vo.BuyEssay;
import com.fbs.wowo.finance.vo.NewBill;
import com.fbs.wowo.finance.vo.WXAppPayResult;
import com.fbs.wowo.manage.service.AccountService;
import com.fbs.wowo.manage.vo.Balance;
import com.fbs.wowo.manage.vo.NamePassVo;
import com.fbs.wowo.manage.vo.Wallet;
import com.fbs.wowo.message.entity.UserMsg;
import com.fbs.wowo.message.mapper.UserMsgMapper;
import com.fbs.wowo.message.service.UserMsgService;
import com.fbs.wowo.message.util.MsgConst;
import com.fbs.wowo.util.common.Constants;
import com.fbs.wowo.util.common.Generator;
import com.fbs.wowo.util.common.Validator;
import com.github.pagehelper.PageHelper;
import com.github.wxpay.sdk.WXOpenConfig;
import com.github.wxpay.sdk.WXPubConfig;
import com.google.zxing.WriterException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service("payService")
@Transactional
public class PayServiceImp implements PayService {
    // 订单状态
    String ORDER_SUCC = "SUCCESS";

    /**
     * 我的购买
     *
     * @param userId 用户id
     * @return BuyEssay
     * @throws InputVerifyFailedException
     */
    @Override
    public List<BuyEssay> buyList(String userId) throws InputVerifyFailedException {
        return paymentMapper.findByBuyerIdPT(userId, OrderGenUtils.ProductType.BUY.getKey());
    }

    /**
     * 我的购买
     * @param userId            用户id
     * @param productCategory   分类
     * @return Buy
     * @throws InputVerifyFailedException 分类输入错误
     *
     * @deprecated 暂时用不上，只有图文音频 使用 {@code buyList} 代替
     */
    @Override
    public List<Buy> buyList(String userId, Byte productCategory) throws InputVerifyFailedException {
        List<Buy> buys = null;
        switch(productCategory) {
            case OrderGenUtils.PCT_ARTICLE:
                // 图文
                // 图片
                // 标题
                // 简介
                // 浏览量
                // id
                buys = paymentMapper.findByBuyerIdPTArt(userId, OrderGenUtils.ProductType.BUY.getKey());
                break;
            case OrderGenUtils.PCT_AUDIO:
                // 音频
                // 图片
                // 标题
                // 时长
                // 浏览量
                // id
                buys = paymentMapper.findByBuyerIdPTAud(userId, OrderGenUtils.ProductType.BUY.getKey());
                break;
            case OrderGenUtils.PCT_ALBUM:
                // 专辑
                // 图片
                // 标题
                // 浏览量
                // 数量
                // id
                buys = paymentMapper.findByBuyerIdPTAlb(userId, OrderGenUtils.ProductType.BUY.getKey());
                break;
            case OrderGenUtils.PCT_LIVE:
                // 直播
                break;
            default:
                throw new InputVerifyFailedException();
        }
        return buys;
    }
    /**
     * 我的 订单列表
     *
     * @param buyerId  买家id
     * @param pageNo   页码
     * @param pageSize 大小
     * @return 订单
     */
    @Override
    public List<Payment> orderList(String buyerId, Integer pageNo, Integer pageSize) {
        // 分类返回数据
        // 图文

        // 音频
        // 专辑
        // 直播
        // 设置
        PageHelper.startPage(pageNo, pageSize);
        List<Payment> list = paymentMapper.findByBuyerId(buyerId, (byte) OrderGenUtils.OrderStatus.SUCCESS.ordinal());
        return list;
    }

    /**
     * 查询是否付过费
     * @param buyerId          用户id
     * @param productId        产品id
     * @param productCategory  打赏或其他
     * @return 1 没付过 2 付过
     */
    @Override
    public Integer paid(String buyerId, String productId, Byte productCategory) {
        Validator.verifyStr(buyerId);
        Validator.verifyStr(productId);
        Validator.verifyNon(productCategory);
        Payment order = findOrder(buyerId, productId, productCategory);
        // 订单不存在或者没有成功支付
        if (order == null || order.getStatus() != (byte) OrderGenUtils.OrderStatus.SUCCESS.ordinal()) return 1;
        return 2;
    }

    /**
     * 余额支付 + 密码校验
     * @param password         支付密码
     * @param buyerId          用户id
     * @param fee              金额
     * @param productId        产品id
     * @param productType      产品类型
     * @param productCategory  打赏或其他
     * @return                 支付状态
     * @throws CreateEntityFailedException  异常
     * @throws InputVerifyFailedException   密码错误
     */
    @Override
    public String submitorderBalanceVerifyPass(String password, String buyerId, BigDecimal fee, String productId, Byte productType, Byte productCategory) throws CreateEntityFailedException, InputVerifyFailedException {
        NamePassVo namePassVo = new NamePassVo();
        namePassVo.setUserId(buyerId);
        namePassVo.setPassword(password);
        accountService.verifyPass(namePassVo);

        return submitorderBalance(buyerId, fee, productId, productType, productCategory);
    }
    /**
     * 余额支付
     * @param buyerId          用户id
     * @param fee              金额
     * @param productId        产品id
     * @param productType      产品类型
     * @param productCategory  产品分类
     * @return                 支付状态
     * @throws CreateEntityFailedException 异常
     */
    @Override
    public String submitorderBalance(String buyerId, BigDecimal fee, String productId, Byte productType, Byte productCategory) throws CreateEntityFailedException {
        Validator.verifyStr(buyerId);
        Validator.verifyMinus(fee);
        Validator.verifyStr(productId);
        Validator.verifyNon(productType);
        Validator.verifyNon(productCategory);
        // 判断订单是否存在
        verifyOrderExist(buyerId, productId, productType, productCategory);

        String[] productInfo = findProductInfo(productId, productCategory);
        String productName = productInfo[0];
        String productOwnerId = productInfo[1];

        byte tradeType = BillGenerator.TradeType.PayOri.getKey();
        // 判断余额
        Wallet wallet = accountService.wallet(buyerId);
        if (fee.subtract(wallet.getBalance()).compareTo(new BigDecimal(0)) > 0) {
            // 余额不足
            Payment pay = generateOrder(buyerId, fee, tradeType, productId, productType, productCategory, productName, OrderGenUtils.OrderStatus.NOTPAY);
            paymentMapper.insertSelective(pay);
            throw new CreateEntityFailedException(ErrorCode.ERR_ASSET_NOT_ENOUGH);
        }

        // 生成订单
        Payment pay = generateOrder(buyerId, fee, tradeType, productId, productType, productCategory, productName, OrderGenUtils.OrderStatus.SUCCESS);
        paymentMapper.insertSelective(pay);
        // 生成账单
        genBill(buyerId, fee, tradeType, productId, productType, productCategory, productName, productOwnerId);

        return ORDER_SUCC;
    }

    /**
     * 扫码支付 NATIVE
     * @param ip                ip
     * @param buyerId           买家id
     * @param fee               费用
     * @param productId         产品id
     * @param productType       产品类型
     * @param productCategory   产品分类
     * @param outputStream      输出到屏幕的输出流
     * @throws WriterException  异常
     * @throws IOException      异常
     */
    @Override
    public void submitorderWXNative(String ip, String buyerId, BigDecimal fee, String productId, Byte productType, Byte productCategory, OutputStream outputStream) throws WriterException, IOException {
        Validator.verifyStr(ip);
        Validator.verifyStr(buyerId);
        Validator.verifyMinus(fee);
        Validator.verifyStr(productId);
        Validator.verifyNon(productType);
        Validator.verifyNon(productCategory);
        Validator.verifyNon(outputStream);
        Map<String, String> result = unifiedorder(ip, buyerId, fee, BillGenerator.TradeType.PayWXNative.getKey(), productId, productType, productCategory);

        // 生成二维码
        String code_url = result.get("code_url");
        QrCodeUtils.writeToStream(code_url, outputStream, 300, 300);
    }

    /**
     *  App支付 APP
     * @param ip                ip
     * @param buyerId           买家id
     * @param fee               费用
     * @param productId         产品id
     * @param productType       产品类型
     * @param productCategory   产品分类
     * @return                  返回给app端结果
     */
    @Override
    public WXAppPayResult submitorderWXApp(String ip, String buyerId, BigDecimal fee, String productId, Byte productType, Byte productCategory) {
        Validator.verifyStr(ip);
        Validator.verifyStr(buyerId);
        Validator.verifyMinus(fee);
        Validator.verifyStr(productId);
        Validator.verifyNon(productType);
        Validator.verifyNon(productCategory);
        Map<String, String> result = unifiedorder(ip, buyerId, fee, BillGenerator.TradeType.PayWXApp.getKey(), productId, productType, productCategory);

        WXAppPayResult wxAppPayResult = new WXAppPayResult();
        Map<String, String> data = new HashMap<>();
        String appid = wxAppPayResult.getAppid();
        String partnerid = wxAppPayResult.getPartnerid();
        String prepay_id = result.get("prepay_id");
        String packkage = wxAppPayResult.getPackkage();
        String nonce_str = result.get("nonce_str");
        // 转换成秒
        String timestamp = new Date().getTime() / 1000 + "";

        data.put("appid", appid);
        data.put("partnerid", partnerid);
        data.put("prepayid", prepay_id);
        data.put("package", packkage);
        data.put("noncestr", nonce_str);
        data.put("timestamp", timestamp);
        String sign = null;
        try {
            sign = WXPayUtils.generateSignature(data, WXOpenConfig.WX_PAY_KEY);
        } catch (Exception e) {
            e.printStackTrace();
        }

        wxAppPayResult.setPrepayid(prepay_id);
        wxAppPayResult.setNoncestr(nonce_str);
        wxAppPayResult.setTimestamp(timestamp);
        wxAppPayResult.setSign(sign);
        return wxAppPayResult;
    }

    /**
     * 微信异步通知
     * @param data  微信传递xml数据
     * @return      通知微信成功信息
     * @throws CreateEntityFailedException 异常
     */
    @Override
    public String wxNotice(String data) throws CreateEntityFailedException {
        Validator.verifyStr(data);
        try {
            // 验证返回码是否正确
            Map<String, String> map = WXPayUtils.verifyReturnXml(data);
            // 验证签名是否正确
            if (!WXPayUtils.isSignatureValid(map, WXOpenConfig.WX_PAY_KEY))
                throw new CreateEntityFailedException(ErrorCode.ERR_PAY_WX_NOTICE_SIGN);

            // 数据库查询，金额是否正确
            String out_trade_no = map.get("out_trade_no");
            Payment pay = paymentMapper.findByTradeNo(out_trade_no);
            if (pay == null) throw new NoSuchDataException(ErrorCode.ERR_PAY_WX_TRADE_NO);
            int total_fee = Integer.parseInt(map.get("total_fee"));
            if (total_fee != MathUtils.parseYuanToFen(pay.getTotalFee())) throw new CreateEntityFailedException(ErrorCode.ERR_PAY_WX_FEE);

            // 如果账单已成功，跳过
            if (pay.getStatus() != OrderGenUtils.OrderStatus.SUCCESS.ordinal()) {
                // 修改数据库订单状态
                updateOrderStatus(pay.getId());
                // 生成账单
                String[] productInfo = findProductInfo(pay.getProductId(), pay.getProductType());
                String productName = productInfo[0];
                String productOwnerId = productInfo[1];
                genBill(pay.getBuyerId(), pay.getTotalFee(), pay.getTradeType(), pay.getProductId(), pay.getProductType(), pay.getProductCategory(), productName, productOwnerId);
            }
            // 返回微信端信息
            Map<String, String> result = new HashMap<>();
            result.put("return_code", "SUCCESS");
            result.put("return_msg", "OK");
            return WXPayUtils.mapToXml(result);
        } catch (Exception e) {
            e.printStackTrace();
            // log
            Map<String, String> result = new HashMap<>();
            result.put("return_code", "FAIL");
            result.put("return_msg", "谅你也看不懂这个错误信息");
            String xml = null;
            try {
                xml = WXPayUtils.mapToXml(result);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            return xml;
        }
    }

    /**
     * 主动查询订单号
     * @param orderNo 订单号
     * @return        交易结果
     */
    @Override
    public String syn(String orderNo) {
        Validator.verifyStr(orderNo);
        Payment pay = paymentMapper.findByTradeNo(orderNo);
        if (pay == null) throw new NoSuchDataException(ErrorCode.ERR_PAY_NO_TRADE);
        // 如果订单状态成功直接返回
        if (pay.getStatus() == OrderGenUtils.OrderStatus.SUCCESS.ordinal()) return ORDER_SUCC;

        // 签名，发送微信端查询订单状态
        Map<String, String> map = new HashMap<>();
        Byte tradeType = pay.getTradeType();
        String key = WXOpenConfig.WX_PAY_KEY;
        if (BillGenerator.TradeType.PayWXNative.getKey() == tradeType) {
            map.put("appid", WXPubConfig.WX_PAY_APP_ID);
            map.put("mch_id", WXPubConfig.WX_PAY_MCH_ID);
        } else {
            map.put("appid", WXOpenConfig.WX_PAY_APP_ID);
            map.put("mch_id", WXOpenConfig.WX_PAY_MCH_ID);
        }
        map.put("transaction_id", pay.getTransactionId());
        map.put("out_trade_no", pay.getTradeNo());
        map.put("nonce_str", WXPayUtils.generateNonceStr());
        try {
            String xml = WXPayUtils.generateSignedXml(map,key);
            String post = HttpUtils.postXml(WXOpenConfig.WX_QUERY_ORDER_URL, xml);
            if (WXPayUtils.isSignatureValid(post, key)) throw new CreateEntityFailedException(ErrorCode.ERR_PAY_WX_QUERY_SIGN);

            Map<String, String> resultMap = WXPayUtils.verifyReturnXml(post);
            int total_fee = Integer.parseInt(resultMap.get("total_fee"));
            if (MathUtils.parseYuanToFen(pay.getTotalFee()) != total_fee) throw new CreateEntityFailedException(ErrorCode.ERR_PAY_WX_QUERY_FEE);

            String trade_state = resultMap.get("trade_state");
            if ("SUCCESS".equals(trade_state)) {
                // 更新订单状态
                updateOrderStatus(pay.getId());
                String[] productInfo = findProductInfo(pay.getProductId(), pay.getProductType());
                String productName = productInfo[0];
                String productOwnerId = productInfo[1];
                // 生成账单
                genBill(pay.getBuyerId(), pay.getTotalFee(), tradeType, pay.getProductId(), pay.getProductType(), pay.getProductCategory(), productName, productOwnerId);
            }
            return trade_state;
        } catch (Exception e) {
            e.printStackTrace();
            throw new CreateEntityFailedException(e.getMessage());
        }
    }

    /**
     * 查询余额
     * @param userId 用户id
     * @return       余额
     */
    @Override
    public Balance balance(String userId) {
        Validator.verifyStr(userId);

        Wallet wallet = accountService.wallet(userId);
        Balance balance = new Balance();
        balance.setBalance(wallet.getBalance());
        return balance;
    }

    /************************************************************
     *
     *
     * 私有方法
     *
     *
     ************************************************************/
    /**
     * 微信统一下单
     * app和扫码支付统一处理
     * @param ip                ip地址
     * @param buyerId           买家id
     * @param fee               费用
     * @param tradeType         支付类型
     * @param productId         产品id
     * @param productType       产品类型
     * @param productCategory   打赏与否
     * @return xml
     */
    private Map<String, String> unifiedorder(String ip, String buyerId, BigDecimal fee, Byte tradeType, String productId, Byte productType, Byte productCategory) {
        // 重复购买
        verifyOrderExist(buyerId, productId, productType, productCategory);

        String[] productInfo = findProductInfo(productId, productCategory);
        String productName = productInfo[0];
        Payment pay = generateOrder(buyerId, fee, tradeType, productId, productType, productCategory, productName, OrderGenUtils.OrderStatus.NOTPAY);

        Map<String, String> reqData = new HashMap<>();
        String key = WXOpenConfig.WX_PAY_KEY;
        // app_id
        if (BillGenerator.TradeType.PayWXNative.getKey() == tradeType) {
            reqData.put("appid", WXPubConfig.WX_PAY_APP_ID);
            reqData.put("mch_id", WXPubConfig.WX_PAY_MCH_ID);

        } else if (BillGenerator.TradeType.PayWXApp.getKey()== tradeType) {
            reqData.put("appid", WXOpenConfig.WX_PAY_APP_ID);
            reqData.put("mch_id", WXOpenConfig.WX_PAY_MCH_ID);
        }
        reqData.put("nonce_str", WXPayUtils.generateNonceStr());
        reqData.put("body", pay.getBody());
        reqData.put("out_trade_no", pay.getTradeNo());
        reqData.put("total_fee", MathUtils.parseYuanToFen(pay.getTotalFee()) + "");
        reqData.put("spbill_create_ip", ip);
        reqData.put("notify_url", WXOpenConfig.WX_NOTIFID_URL);
        reqData.put("trade_type", BillGenerator.TradeType.getValue(tradeType));
        if (BillGenerator.TradeType.PayWXNative.getKey() == tradeType)
            reqData.put("product_id", pay.getProductId().replaceAll("-", ""));
        Map<String, String> result = null;
        try {
            String signedXml = WXPayUtils.verifySignedXml(reqData, key);
            // 发送请求
            String post = HttpUtils.postXml(WXOpenConfig.WX_PAY_ORDER, signedXml);
            result = WXPayUtils.verifyReturnXml(post);
        } catch (CreateEntityFailedException e) {
            e.printStackTrace();
            throw new CreateEntityFailedException(e.getMsg());
        } catch (IOException e) {
            e.printStackTrace();
            throw new CreateEntityFailedException(e.getMessage());
        }
        paymentMapper.insertSelective(pay);
        return result;
    }

    /**
     * 生成存储到数据库的订单
     * @param buyerId           买家id
     * @param fee               费用
     * @param tradeType         支付类型
     * @param productId         产品id
     * @param productType       打赏与否
     * @param productCategory   产品类型
     * @param productName       产品名称
     * @param status            支付状态
     */
    private Payment generateOrder(String buyerId, BigDecimal fee, Byte tradeType, String productId, Byte productType, Byte productCategory, String productName, OrderGenUtils.OrderStatus status) {
        //
        Payment pay = new Payment();
        pay.setBody(productName);
        String prefix = "";
        if (BillGenerator.TradeType.PayWXNative.getKey() == tradeType || BillGenerator.TradeType.PayWXApp.getKey() == tradeType) {
            prefix = "W";
        } else if (BillGenerator.TradeType.PayOri.getKey() == tradeType){
            prefix = "0";
        }
        pay.setTradeNo(
                OrderGenUtils.tradeNo(
                        OrderGenUtils.ProductType.getValue(productType),
                        OrderGenUtils.ProductCategory.getValue(productCategory),
                        prefix + BillGenerator.TradeType.getValue(tradeType).charAt(0)));
        pay.setTotalFee(fee);
        pay.setTimeStart(new Date());
        pay.setTradeType(tradeType);
        pay.setProductId(productId);
        pay.setProductCategory(productCategory);

        pay.setBuyerId(buyerId);
        pay.setStatus((byte) status.ordinal());
        pay.setProductType(productType);
        // 订单id
        pay.setId(Generator.generateId());
        return pay;
    }

    /**
     * 生成账单
     * @param buyerId           买家id
     * @param fee               费用
     * @param tradeType         支付类型
     * @param productId         产品id
     * @param productType       打赏与否
     * @param productCategory   产品类型
     * @param productName       产品名称
     * @param productOwnerId    产品发布者id
     */
    private void genBill(
            String buyerId,
            BigDecimal fee,
            Byte tradeType,
            String productId,
            Byte productType,
            Byte productCategory,
            String productName,
            String productOwnerId) {
        // 生成用户支付账单
        String buyScene = BillGenerator.genScene(
                productName,
                productType,
                productCategory,
                BillGenerator.Income_Outcome);
        genBillDetail(buyerId,
                fee,
                BillGenerator.Income_Outcome,
                productOwnerId,
                tradeType,
                productId,
                productType,
                productCategory,
                buyScene);

        // 卖家增加收入
        BigDecimal income = fee.multiply(BillGenerator.Percentage);
        String sellScene = BillGenerator.genScene(
                productName,
                productType,
                productCategory,
                BillGenerator.Income_Income);
        genBillDetail(buyerId,
                income,
                BillGenerator.Income_Income,
                productOwnerId,
                tradeType,
                productId,
                productType,
                productCategory,
                sellScene);

        // 推送消息
        sendMsg(productOwnerId, productId);
    }

    private void sendMsg(String userId, String enId) {
        Essay essay = essayMapper.selectByPrimaryKey(enId);
        UserMsg userMsg = new UserMsg();
        userMsg.setUserId(userId);
        userMsg.setMsgType(MsgConst.MsgBuy);
        userMsg.setMsgContent("有新的用户购买了你的 " + essay.getTitle());
        userMsg.setEntityId(enId);
        userMsg.setEntityType(MsgConst.EnEssay);
        userMsgService.add(userMsg);
    }

    /**
     * 修改订单状态为成功
     * @param payId 订单id
     */
    private void updateOrderStatus(String payId) {
        // 修改订单状态
        Payment record = new Payment();
        record.setId(payId);
        record.setStatus((byte) OrderGenUtils.OrderStatus.SUCCESS.ordinal());
        paymentMapper.updateByPrimaryKeySelective(record);
    }

    /**
     * 生成账单详情
     * @param buyerId           买家id
     * @param fee               费用
     * @param isIncome          收入&支出
     * @param productOwnerId    产品发布者id
     * @param tradeType         支付类型
     * @param productId         产品id
     * @param productType       产品类型
     * @param productCategory   打赏与否
     * @param scene             场景
     */
    private void genBillDetail(
            String buyerId,
            BigDecimal fee,
            Byte isIncome,
            String productOwnerId,
            Byte tradeType,
            String productId,
            Byte productType,
            Byte productCategory,
            String scene) {
        NewBill newBill = new NewBill();
        // 获取余额
        Wallet wallet;
        if (isIncome == BillGenerator.Income_Income) {
            wallet = accountService.wallet(productOwnerId);
            newBill.setUserId(productOwnerId);
            newBill.setOtherSide(buyerId);
        } else {
            wallet = accountService.wallet(buyerId);
            newBill.setUserId(buyerId);
            newBill.setOtherSide(productOwnerId);
        }
        newBill.setTotalFee(fee);
        newBill.setIsIncome(isIncome);
        newBill.setRefund("");
        // 获取操作前余额
        BigDecimal balance = wallet.getBalance();
        newBill.setBalance(balance);
        newBill.setProductId(productId);
        newBill.setProductType(productType);
        newBill.setProductCategory(productCategory);
        newBill.setStatus(BillGenerator.StatusSuccess);
        newBill.setScene(scene);
        newBill.setTip("");
        newBill.setAccessType(BillGenerator.AccessNo);
        newBill.setTradeType(tradeType);
        billService.add(newBill);
    }

    /**
     * 查询产品名称和作者id
     * @param productId String
     * @param productCategory Byte
     */
    private String[] findProductInfo(String productId, Byte productCategory) {
        String productName = null;
        String userId = null;
        // 数据库查找产品 和 用户id
        switch (productCategory) {
            case (Constants.ENTITY_TYPE_ARTICLE):
            case (Constants.ENTITY_TYPE_VOICE):
                Essay essay = essayMapper.selectByPrimaryKey(productId);
                if (essay == null) throw new NoSuchDataException();
                productName = essay.getTitle();
                userId = essay.getUserId();
                break;
        }
        String[] productInfo = new String[2];
        productInfo[0] = productName;
        productInfo[1] = userId;
        return productInfo;
    }

    /**
     * 验证订单是否存在
     * @param buyerId           买家id
     * @param productId         产品id
     * @param productCategory   产品分类
     */
    private void verifyOrderExist(String buyerId, String productId, Byte productType, Byte productCategory) {
        if (OrderGenUtils.ProductType.REWARD.getKey() == productType) return;
        // 判断是否重复支付 根据buyerId, productCategory, productType, productId判断
        Payment pay = findOrder(buyerId, productId, productCategory);
        if (pay == null) return;
        // 订单已存在并且已成功
        if (pay.getStatus() == OrderGenUtils.OrderStatus.SUCCESS.ordinal()) throw new EntityHasExistException(ErrorCode.ERR_PAY_WX_SUBMIT_ORDER);
    }

    /**
     * 按买家id，产品id，产品类型来查找购买的记录，判断是否已购买
     * @param buyerId           买家id
     * @param productId         产品id
     * @param productCategory   产品类型
     * @return Payment
     */
    private Payment findOrder(String buyerId, String productId, Byte productCategory) {
        return paymentMapper.findByBuyerProductIdTypeCT(
                    buyerId,
                    productId,
                    OrderGenUtils.ProductType.BUY.getKey(),
                    productCategory);
    }

    @Autowired
    private PaymentMapper paymentMapper;
    @Autowired
    private BillService billService;
    @Autowired
    private EssayMapper essayMapper;
    @Autowired
    private AccountService accountService;
    @Autowired
    private UserMsgService userMsgService;
}
